package com.california.pay.common.trace;

import com.alibaba.ttl.TransmittableThreadLocal;
import org.slf4j.MDC;

/**
 * 跟踪线程本地变量
 */
public class TraceThreadLocal {
    /**
     * 跟踪号
     */
    public static final String TRACE_ID = "_TRACE_ID_";
    /**
     * 日志中的关键字，使用%X{traceId}打印日志
     */
    public static final String MDC_TRACE_KEY = "traceId";

    /**
     * 跟踪号本地变量
     * 这里使用TransmittableThreadLocal，为了使子线程get的时候可以得到父线程的traceId，
     * 普通的创建线程无需做更改；如果使用线程池，有三种方式能够保证传递值
     * 2.1 修饰Runnable和Callable
     * 2.2 修饰线程池
     * 2.3 使用Java Agent来修饰JDK线程池实现类
     * 详见https://github.com/alibaba/transmittable-thread-local
     */
    private static final ThreadLocal<String> TRACE_ID_THREAD_LOCAL = new TransmittableThreadLocal<>();


    public static String getTraceId() {
        return TRACE_ID_THREAD_LOCAL.get();
    }

    public static void setTraceId(String traceId) {
        TRACE_ID_THREAD_LOCAL.set(traceId);
        MDC.put(MDC_TRACE_KEY, traceId);
    }

    /**
     * 设置TraceId，如果TraceId还未设置过的话。
     * 未设置过，说明调用处是调用入口位置；设置过，说明只是调用过程中的一个节点。
     *
     * @param traceId
     * @return
     */
    public static boolean setTraceIdIfNotTracing(String traceId) {
        String parentTraceId = getTraceId();
        if (parentTraceId == null) {
            setTraceId(traceId);
            return true;
        } else {
            return false;
        }
    }

    /**
     * 是否在跟踪
     *
     * @return
     */
    public static boolean isTracing() {
        return getTraceId() != null;
    }

    public static void clear() {
        TRACE_ID_THREAD_LOCAL.remove();
        MDC.clear();
    }
}
